﻿using System;
using static System.Math;

namespace WindLoad
{
    public enum GroundType
    {
        A,
        B,
        C,
        D
    }

    public enum RiskArea
    {
        R1,
        R2,
        R3
    }

    public static class EnumExtension
    {
        public static int Index<TEnum>(this TEnum v) where TEnum : Enum
            => (int) (object) v;
    }

    public enum ForceUnit
    {
        N = 1,
        KN = 1000,
    }

    public static class ForceUnitExtension
    {
        public static double Value(this ForceUnit forceUnit) => 1D / (int) forceUnit;
    }

    /// <summary>
    /// 设计基准风速
    /// </summary>
    public static class WindSpeed
    {
        private static readonly double[] DataAlpha0 = {0.12, 0.16, 0.22, 0.30};
        public static double GetAlpha0(GroundType groundType) => DataAlpha0[groundType.Index()];

        public static RiskArea GetRiskArea(double u10) =>
            u10 > 32.6 ? RiskArea.R1 : (u10 > 24.5 ? RiskArea.R2 : RiskArea.R3);

        private static readonly double[] DataKf = {1.05, 1.02, 1.0};
        public static double GetKf(RiskArea riskArea) => DataKf[riskArea.Index()];

        /// <summary>
        /// 桥梁或构件基准高度Z处的设计基准风速Ud
        /// </summary>
        /// <param name="us10">桥梁设计基本风速</param>
        /// <param name="z">设计基准高度</param>
        /// <param name="groundType"></param>
        /// <param name="riskArea"></param>
        /// <returns></returns>
        public static double GetUd(double us10, double z, GroundType groundType, RiskArea riskArea)
            => GetKf(riskArea) * Pow(z / 10D, GetAlpha0(groundType)) * us10;

        /// <summary>
        /// 由Ud反算得到Us10
        /// </summary>
        /// <param name="ud">水面以上z高度处的基本风速</param>
        /// <param name="z"></param>
        /// <param name="groundType"></param>
        /// <param name="riskArea"></param>
        /// <returns></returns>
        public static double GetUs10FromUd(double ud, double z, GroundType groundType, RiskArea riskArea)
            => ud / (GetKf(riskArea) * Pow(z / 10D, GetAlpha0(groundType)));
    }

    /// <summary>
    /// 桁架式主梁风荷载
    /// </summary>
    public static class TrussGirderWind
    {
        private static readonly double[] DataHorizontalLength =
            {20, 60, 100, 200, 300, 400, 500, 650, 800, 1000, 1200, 1500, 2000};

        private static readonly double[,] DataGv =
        {
            {1.29, 1.28, 1.26, 1.24, 1.23, 1.22, 1.21, 1.20, 1.19, 1.18, 1.17, 1.16, 1.15},
            {1.35, 1.33, 1.31, 1.29, 1.27, 1.26, 1.25, 1.24, 1.23, 1.22, 1.21, 1.20, 1.18},
            {1.49, 1.48, 1.45, 1.41, 1.39, 1.37, 1.36, 1.34, 1.33, 1.31, 1.30, 1.29, 1.26},
            {1.56, 1.54, 1.51, 1.47, 1.44, 1.42, 1.41, 1.39, 1.37, 1.35, 1.34, 1.32, 1.30},
        };

        public static double GetGv(double girderSpan, GroundType groundType)
            => DataGv.GetRow(groundType.Index())
                .Interpolate(DataHorizontalLength.GetIndex(girderSpan));

        public static double GetUg(double ud, double girderSpan, GroundType groundType)
            => ud * GetGv(girderSpan, groundType);

        public static readonly double[] DataAreaRatio = {0.1, 0.2, 0.3, 0.4, 0.5};
        public static readonly double[] DataDistRatio = {1D, 2D, 3D, 4D, 5D};

        // 矩形与H形构件
        public static readonly double[] DataCh = {1.9, 1.8, 1.7, 1.7, 1.6};

        /// <summary>
        /// 桁架的横向力系数
        /// </summary>
        /// <param name="areaRatio">实面积比</param>
        /// <returns></returns>
        public static double GetCh(double areaRatio)
            => DataCh.Interpolate(DataAreaRatio.GetIndex(areaRatio));

        public static readonly double[,] DataEta =
        {
            {1.0, 0.90, 0.80, 0.60, 0.45},
            {1.0, 0.90, 0.80, 0.65, 0.50},
            {1.0, 0.95, 0.80, 0.70, 0.55},
            {1.0, 0.95, 0.80, 0.70, 0.60},
            {1.0, 0.95, 0.85, 0.75, 0.65},
            {1.0, 0.95, 0.90, 0.80, 0.70},
        };

        public static double GetEta(int beamCount, double distRatio, double areaRatio)
            => beamCount <= 1
                ? 1D
                : DataEta.Interpolate(DataDistRatio.GetIndex(distRatio), DataAreaRatio.GetIndex(areaRatio));

        /// <summary>
        /// 单片桁架梁横桥向每米所受横向力(N/m)
        /// </summary>
        /// <param name="ug">等效静阵风风速</param>
        /// <param name="beamHeight">迎风桁架高度</param>
        /// <param name="beamDist">两桁架中心距</param>
        /// <param name="beamCount">桁架片数</param>
        /// <param name="areaRatio">实面积比</param>
        /// <param name="rho"></param>
        /// <returns></returns>
        public static double GetTransverseFg(double ug, double beamHeight, double beamDist, int beamCount,
            double areaRatio, double rho = 1.25)
            => 0.5 * rho * ug * ug * GetCh(areaRatio) * GetEta(beamCount, beamDist / beamHeight, areaRatio) *
               beamHeight;

        /// <summary>
        /// 桁架梁纵桥向每米所受纵向力(N/m)
        /// </summary>
        /// <param name="ug">等效静阵风风速</param>
        /// <param name="cf">摩擦系数</param>
        /// <param name="perimeter">梁体横断面外轮廓周长</param>
        /// <param name="rho"></param>
        /// <returns></returns>
        public static double GetLongitudinalFg(double ug, double cf, double perimeter, double rho = 1.25)
            => 0.5 * rho * ug * ug * cf * perimeter;
    }

    /// <summary>
    /// 桥塔风荷载
    /// </summary>
    public static class TowerWind
    {
        private static readonly double[] DataTowerHeight =
            {40, 60, 80, 100, 150, 200, 300, 400};

        private static readonly double[,] DataGv =
        {
            {1.19, 1.18, 1.17, 1.16, 1.14, 1.13, 1.12, 1.11},
            {1.24, 1.22, 1.20, 1.19, 1.17, 1.16, 1.14, 1.13},
            {1.33, 1.29, 1.27, 1.26, 1.23, 1.21, 1.18, 1.16},
            {1.48, 1.42, 1.39, 1.36, 1.31, 1.28, 1.24, 1.22},
        };

        public static double GetGv(double towerHeight, GroundType groundType)
            => DataGv.GetRow(groundType.Index())
                .Interpolate(DataTowerHeight.GetIndex(towerHeight));

        public static double GetUg(double ud, double towerHeight, GroundType groundType)
            => ud * GetGv(towerHeight, groundType);

        private static readonly double[] DataTw = {1D / 4D, 1D / 3D, 2D / 3D, 1D, 1.5, 2, 3, 4};
        private static readonly double[] DataHw = {1D, 2D, 4D, 6D, 10D, 20D, 40D};

        private static readonly double[,] DataCd =
        {
            {1.3, 1.4, 1.5, 1.6, 1.7, 1.9, 2.1},
            {1.3, 1.4, 1.5, 1.6, 1.8, 2.0, 2.2},
            {1.3, 1.4, 1.5, 1.6, 1.8, 2.0, 2.2},
            {1.2, 1.3, 1.4, 1.5, 1.6, 1.8, 2.0},
            {1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.7},
            {0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4},
            {0.8, 0.8, 0.8, 0.9, 0.9, 1.0, 1.2},
            {0.8, 0.8, 0.8, 0.8, 0.8, 0.9, 1.1},
        };

        public static double GetCd(double tw, double hw)
            => DataCd.Interpolate(DataTw.GetIndex(tw), DataHw.GetIndex(hw));

        /// <summary>
        /// 横桥向桥塔构件单位长度上的风荷载
        /// </summary>
        /// <param name="ug"></param>
        /// <param name="towerHeight"></param>
        /// <param name="t">横桥向宽度</param>
        /// <param name="w">横桥向厚度</param>
        /// <param name="rho"></param>
        /// <returns></returns>
        public static double GetTransverseFg(double ug, double towerHeight, double t, double w, double rho = 1.25)
            => 0.5 * rho * ug * ug * GetCd(t / w, towerHeight / w) * w;

        /// <summary>
        /// 纵桥向桥塔构件单位长度上的风荷载
        /// </summary>
        /// <param name="ug"></param>
        /// <param name="towerHeight"></param>
        /// <param name="t">横桥向宽度</param>
        /// <param name="w">横桥向厚度</param>
        /// <param name="rho"></param>
        /// <returns></returns>
        public static double GetLongitudinalFg(double ug, double towerHeight, double t, double w, double rho = 1.25)
            => 0.5 * rho * ug * ug * GetCd(w / t, towerHeight / t) * t;
    }

    public static class CableWind
    {
        private static readonly double[] DataHorizontalLength =
            {20, 60, 100, 200, 300, 400, 500, 650, 800, 1000, 1200, 1500, 2000};

        private static readonly double[,] DataGv =
        {
            {1.29, 1.28, 1.26, 1.24, 1.23, 1.22, 1.21, 1.20, 1.19, 1.18, 1.17, 1.16, 1.15},
            {1.35, 1.33, 1.31, 1.29, 1.27, 1.26, 1.25, 1.24, 1.23, 1.22, 1.21, 1.20, 1.18},
            {1.49, 1.48, 1.45, 1.41, 1.39, 1.37, 1.36, 1.34, 1.33, 1.31, 1.30, 1.29, 1.26},
            {1.56, 1.54, 1.51, 1.47, 1.44, 1.42, 1.41, 1.39, 1.37, 1.35, 1.34, 1.32, 1.30},
        };

        public static double GetGv(double horizontalLength, GroundType groundType)
            => DataGv.GetRow(groundType.Index())
                .Interpolate(DataHorizontalLength.GetIndex(horizontalLength));

        public static double GetUg(double ud, double horizontalLength, GroundType groundType)
            => ud * GetGv(horizontalLength, groundType);

        /// <summary>
        /// 横桥向斜拉索单位长度等效静阵风荷载
        /// </summary>
        /// <param name="ug"></param>
        /// <param name="cd">斜拉索阻力系数</param>
        /// <param name="dc">斜拉索外径</param>
        /// <param name="rho"></param>
        /// <returns></returns>
        public static double GetTransverseFg(double ug, double cd, double dc, double rho = 1.25)
            => 0.5 * rho * ug * ug * cd * dc;

        /// <summary>
        /// 顺桥向斜拉索单位长度水平向风荷载
        /// </summary>
        /// <param name="ug"></param>
        /// <param name="cd">斜拉索阻力系数</param>
        /// <param name="dc">斜拉索外径</param>
        /// <param name="alphac">斜拉索的倾斜角</param>
        /// <param name="rho"></param>
        /// <returns></returns>
        public static double GetLongitudinalFg(double ug, double cd, double dc, double alphac, double rho = 1.25)
            => 0.5 * rho * ug * ug * cd * dc * Pow(Sin(alphac), 2);
    }
}